from bulkfood_v6 import LineItem

# 类元编程  除非开发框架，否则不要编写元类
# 类元编程是指在运行时创建或定制类的技艺
# 在 Python 中，类是一等对象，因此任何时候都可以使用函数新建类，而无需使用 class 关键字。

# 1、类工厂函数
# @see record_factory.py
# 2、定制描述符的类装饰器
# @see bulkfood_v6.py model_v6.py
# 只对直接依附的类有效。这意味着，被装饰的类的子类可能继承也可能不继承装饰器所做的改动
# 3、导入时和运行时比较
# @see evaltime.py
# 导入import evaltime
# <[100]> evalsupport module start # evalsupport模块中的所有顶层代码在导入模块时运行 解释器会编译deco_alpha函数，但是不会执行定义体
# <[400]> MetaAleph body # 类的定义体都执行了 包括嵌套的类
# <[700]> evalsupport module end
# <[1]> evaltime module start
# <[2]> ClassOne body
# <[6]> ClassTwo body
# <[7]> ClassThree body
# <[200]> deco_alpha # 先计算被装饰的类 ClassThree 的定义体，然后运行装饰器函数
# <[9]> ClassFour body
# <[14]> evaltine module end
#  python3 evaltime.py
# ...9前面和上面一样
# <[9]> ClassFour body
# <[11]> ClassOne tests ..............................
# <[3]> ClassOne.__init__
# <[5]> ClassOne.method_x
# <[12]> ClassThree tests ..............................
# <[300]> deco_alpha:inner_1 # deco_alpha 装饰器修改了 ClassThree.method_y 方法，因此调用 three.method_y() 时会运行 inner_1 函数的定义体
# <[13]> ClassFour tests ..............................
# <[10]> ClassFour.method_y
# <[14]> evaltine module end
# <[4]> ClassOne.__del__ # 只有程序结束时，绑定在全局变量 one 上的 ClassOne 实例才会被垃圾回收程序回收

# 4、元类基础知识
# 元类是制造类的工厂，不过不是函数而是类
# 根据 Python 对象模型，类是对象，因此类肯定是另外某个类的实例
# 默认情况下，Python 中的类是 type 类的实例。也就是说，type 是大多数内置的类和用户定义的类的元类
print('spam'.__class__)
print(str.__class__)
print(LineItem.__class__)
print(type.__class__)
# 所有类都是 type 的实例，但是元类还是 type的子类，因此可以作为制造类的工厂
# >>> import evaltime_meta
# <[100]> evalsupport module start
# <[400]> MetaAleph body
# <[700]> evalsupport module end
# <[1]> evaltime_meta  module start
# <[2]> ClassThree body
# <[200]> deco_alpha
# <[4]> ClassFour body
# <[6]> ClassFive body
# <[500]> MetaAleph.__init__ # 创建 ClassFive 时调用了MetaAleph.__init__ 方法
# <[9]> ClassSix body
# <[500]> MetaAleph.__init__ # 创建 ClassFive 时调用了MetaAleph.__init__ 方法
# <[15]> evaltine module end
#  python .\evaltime_meta.py
# ...上面一样
# <[500]> MetaAleph.__init__
# <[11]> ClassThree tests ..............................
# <[300]> deco_alpha:inner_1
# <[12]> ClassFour tests ..............................
# <[5]> ClassFour.method_y
# <[13]> ClassFive tests ..............................
# <[7]> ClassFive.__init__
# <[600]> MetaAleph.__init__:inner_2 # MetaAleph 类的 __init__ 方法把 ClassFive.method_z 方法替换成 inner_2 函数。
# <[14]> ClassSix tests ..............................
# <[7]> ClassFive.__init__
# <[600]> MetaAleph.__init__:inner_2 # ClassFive 的子类 ClassSix 也是一样，method_z 方法被替换成 inner_2 函数
# <[15]> evaltine module end

# 5、定制描述符的元类
# @see bulkfood_v7.py

# 6、元类的特殊方法 __prepare__  只在元类中有用，而且必须声明为类方法
# 解释器调用元类的 __new__ 方法之前会# 先调用 __prepare__ 方法，使用类定义体中的属性创建映
# 射。__prepare__ 方法的第一个参数是元类，随后两个参数分别是要构建的类的名称和基类组成的元组，返回值必须是映射
# 元类构建新类时，__prepare__ 方法返回的映射会传给 __new__ 方法的最后一个参数，然后再传给 __init__ 方法
# @see model_v8.py
# 在现实世界中，框架和库会使用元类协助程序员执行很多任务，例如：
# 验证属性
# 一次把装饰器依附到多个方法上
# 序列化对象或转换数据
# 对象关系映射
# 基于对象的持久存储
# 动态转换使用其他语言编写的类结构

# 7、类作为对象
# cls.__bases__
# 由类的基类组成的元组
# cls.__qualname__
# 其值是类或函数的限定名称，即从模块的全局作用域到类的点分路径。
# 例如，在示例 21-6 中，内部类ClassTwo 的 __qualname__ 属性，其值是字符串
# 'ClassOne.ClassTwo'，而 __name__ 属性的值是 'ClassTwo'
# cls.__subclasses__()
# 这个方法返回一个列表，包含类的直接子类。这个方法的实现使用弱引用，
# 防止在超类和子类（子类在 __bases__ 属性中储存指向超类的强引用）之间出现循环引用。
# 这个方法返回的列表中是内存里现存的子类
# cls.mro()
# 构建类时，如果需要获取储存在类属性 __mro__ 中的超类元组，
# 解释器会调用这个方法。元类可以覆盖这个方法，定制要构建的类解析方法的顺序
